package com.winner.data.util;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import cn.hutool.core.collection.ListUtil;
import com.google.common.collect.Sets;
import com.winner.data.entity.User;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 两个集合的和 和 集合的并集 是不一样的, 可能在一些情况下(如两个集合元素都不同)他们的结果相同
 * <p>
 * 求2个集合去重后的交集(用来更新)、差集(用来保存) 并集(A集合添加A不包含B集合的元素,最终的A集合就是并集,即包含两个集合中所有不重复元素的新集合) 对于两个10w的list取交集 效率高低排序
 * com.google.common.collect.Sets.intersection() > set.contains(a.getName()) 或 hm.containsKey() >
 * list1.stream().filter#list2.anymatch
 * <p>
 * Sets.intersection() 20w 0.083 set.contain() 20w 0.97 普通list anymatch() 20w 0.1291771
 *
 * list为null,那么list.stream()就会报空指针错误, 所以确保list不为null再用stream()
 *
 * @author wangxinle5
 * @since 2022/04/25
 */
@Slf4j
public class ListInsectionDifference {

    public static void main(String[] args) {
        // 不推荐如下写法, 建议直接用 new ArrayList<>(Sets.intersection(new HashSet<>(list1), new HashSet<>(list2)));
        List<String> unionStrList = Stream.concat(ListUtil.toList("1").stream(), ListUtil.toList("2")
                .stream()).distinct().collect(Collectors.toList());
        System.out.println(unionStrList);

        // 求list1 与 list2的 交集/差集
        ArrayList<User> list1 = new ArrayList<>();
        list1.add(new User(1L, "张三"));
        list1.add(new User(2L, "李四"));

        ArrayList<User> list2 = new ArrayList<>();
        list2.add(new User(1L, "张11三"));

        // 两个list 如果某2个字段相等, 就把为第二个list的值 设置给第一个list的对象
        list1.forEach(a -> {
            list2.stream().filter(b -> a.getId().equals(b.getId())).findFirst().ifPresent(s -> {
                a.setName(s.getName());
            });
        });
        // list和一个对象某个字段相等, 把值设置到对象里
        User user = new User(1L, "张三");
        list1.stream().filter(b -> b.getId().equals(user.getId())).findFirst().ifPresent(s -> {
            user.setName(s.getName());
        });

        List<User> unionList = new ArrayList<>(Sets.union(new HashSet<>(list1), new HashSet<>(list2)));
        System.out.println("并集是: " + unionList);

        // 使用com.google.common.collect.Sets包的intersection()或difference() 必须重写 equals and hashcode 方法
        List<User> intersectionList = new ArrayList<>(Sets.intersection(new HashSet<>(list1), new HashSet<>(list2)));
        System.out.println("交集是: " + intersectionList);
        List<User> differenceList = new ArrayList<>(Sets.difference(new HashSet<>(list1), new HashSet<>(list2)));
        System.out.println("差集是: " + differenceList);

        // anyMatch 判断数据列表中是否存在任意一个元素符合设置的predicate条件
        // noneMatch 判断数据列表中全部元素都不符合设置的predicate条件
        // allMatch 判断数据列表中全部元素都符合设置的predicate条件

        // java8求交集 set.contains比anyMatch/noneMatch效率高, 因为Set的查找操作比List更快。你可以将list中的元素添加到Set中，然后执行差集操作
        Set<String> set = list2.stream().map(User::getName).collect(Collectors.toSet());
        List<User> collect = list1.stream().filter(a -> set.stream().anyMatch(b -> b.equalsIgnoreCase(a.getName())))
            .collect(Collectors.toList());
        System.out.println("set求交集是: " + collect);

        // java8求交集 anyMatch
        List<User> intersectionListByJava8 =
            list1.stream().filter(a -> list2.stream().anyMatch(b -> StrUtil.equalsIgnoreCase(a.getName(), b.getName())))
                .collect(Collectors.toList());
        System.out.println("java8交集是: " + intersectionListByJava8);

        // java8求差集 noneMatch
        List<User> differenceListByJava8 = list1.stream()
            .filter(a -> list2.stream().noneMatch(b -> StrUtil.equalsIgnoreCase(a.getName(), b.getName())))
            .collect(Collectors.toList());
        System.out.println("java8差集是: " + differenceListByJava8);

        // 一个list去重 方法一, 需要重写equals and hashcode 方法
        ArrayList<User> list = new ArrayList<>();
        list.add(new User(1L, "张三"));
        list.add(new User(1L, "张三"));
        List<User> dintinctList = new ArrayList<>(new HashSet<>(list));
        System.out.println("HashSet 实现去重(无序), 结果是: " + dintinctList);

        // 一个list去重 方法二 不需要重写equals and hashcode 方法
        ArrayList<User> distinctList = list.stream()
            .collect(Collectors.collectingAndThen(
                Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getId() + o.getName()))),
                ArrayList::new));

        System.out.println("Stream表达式去重(有序的), 结果是: " + distinctList);

    }

}
